/*
 * Copyright (c) 2024 Huawei Device Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/**
 * AppStorageV2
 *
 * The AppStorageV2 provides a key-value database in memory.
 *
 * Main Features:
 * - Can be imported from anywhere in the app to access the same keys and their values.
 * - Database content in memory only.
 * - When app terminates the database content is lows.
 *
 * Usage Scenarios:
 * - Store the object in memory.
 * - Store the key-value pairs in state management observable view models (e.g., @ObservedV2 class objects).
 *
 * The name 'AppStorageV2' is derived from the 'AppStorage', reflecting its purpose to enhance AppStorage.
 *
 */
class AppStorageV2 {
  /**
   * If the value for the given key is already available, return the value.
   * If not, add the key with value generated by calling defaultFunc and return it to caller.
   *
   * @template T - The original object.
   * @param { { new(...args: any): T } } type - The type of the stored value.
   * @param { string | StorageDefaultCreator<T> } [keyOrDefaultCreator] - The alias name of the key, or function generating the default value.
   * @param { StorageDefaultCreator<T> } [defaultCreator] - The function generating the default value.
   * @returns { T } The value of the existed key or the default value.
   */
  static connect(type, keyOrDefaultCreator, defaultCreator) {
    return AppStorageV2.appStorageV2Impl_.connect(type, keyOrDefaultCreator, defaultCreator);
  }

  /**
   * Removes the given key. Return false if the given key does not exist.
   *
   * @param { string | { new(...args: any): T } } keyOrType - Key or class type removing.
   */
  static remove(keyOrType) {
    AppStorageV2.appStorageV2Impl_.remove(keyOrType);
  }

  /**
   * Return the array of all keys.
   *
   * @returns { Array<string> } The array of all keys.
   */
  static keys() {
    return AppStorageV2.appStorageV2Impl_.getMemoryKeys();
  }
}

AppStorageV2.appStorageV2Impl_ =
InteropConfigureStateMgmt.needsInterop() ? InteropStorageV2.instance() : AppStorageV2Impl.instance();

/**
 * PersistenceV2
 *
 * The PersistenceV2 provides a key-value database in disk.
 *
 * Main Features:
 * - Key and their values are written to disk.
 * - Keys and values will be available on application re-start.
 *
 * Usage Scenarios:
 * - It is only for @ObservedV2 class objects.
 *
 * The name 'PersistenceV2' is derived from the 'PersistentStorage', reflecting its purpose to enhance PersistentStorage.
 *
 */
class PersistenceV2 extends AppStorageV2 {
  /**
   * Instructs to persis given (nested) @ObservedV2 class object whenever one of its @Trace decorated properties changes.
   * If value for given key is available already, return its value. if not, add key with value generated by callling defaultCreator and return it.
   * It is also allowed to supply an object other than @ObservedV2 class object to connect.
   * Only in this case need to call the save function manually.
   * use module path to store data in disk
   *
   * @template T - The original object.
   * @param { { new(...args: any): T } } type - The type of the stored value.
   * @param { string | StorageDefaultCreator<T> } [keyOrDefaultCreator] - The alias name of the key, or function generating the default value.
   * @param { StorageDefaultCreator<T> } [defaultCreator] - The function generating the default value.
   * @returns { T } The value of the existed key or the default value.
   */
  static connect(type, keyOrDefaultCreator, defaultCreator) {
    return PersistenceV2.persistenceV2Impl_.connect(type, keyOrDefaultCreator, defaultCreator);
  }

    /**
   * Instructs to persis given (nested) @ObservedV2 class object whenever one of its @Trace decorated properties changes.
   * If value for given key is available already, return its value. if not, add key with value generated by callling defaultCreator and return it.
   * It is also allowed to supply an object other than @ObservedV2 class object to connect.
   * Only in this case need to call the save function manually.
   * use application path to store data in disk
   *
   * @template T - The original object.
   * @param { { ConnectOptions<T extends Objects>: T } } connectOptions - Connect param.
   * @returns { T } The value of the existed key or the default value.
   */
  static globalConnect(connectOptions) {
    return PersistenceV2.persistenceV2Impl_.globalConnect(connectOptions);
  }

  /**
   * The purpose of the save function is request to persist non-observed object changes.
   * For example, change to object properties without @Trace decotration.
   * The app needs to call save each time after an object has been changes.
   *
   * @param { string | { new(...args: any): T } } keyOrType key or class type need to save, that persist non-observed object changes
   */
  static save(keyOrType) {
    PersistenceV2.persistenceV2Impl_.save(keyOrType);
  }

  /**
   * Removes the persistence of given key.
   * It remove given key from memory cache and from disk.
   *
   * @param { string | { new(...args: any): T } } keyOrType - Key or class type removing.
   */
  static remove(keyOrType) {
    PersistenceV2.persistenceV2Impl_.remove(keyOrType);
  }

  /**
   * Return the array of all persisted keys.
   *
   * @returns { Array<string> } The array of all keys.
   */
  static keys() {
    return PersistenceV2.persistenceV2Impl_.keys();
  }

  /**
   * Application can use the function to register its own callback function.
   * The framework will call the function when persisting has encoutered an error.
   * The supplied parameters in function call are:
   * key: of the key-value pair that caused the error
   * reason: quota or serialisation or unknown failure reason in enum format
   * message: more explanation of the reason of failure
   * Calling notifyOnError with undefined unregisters from receiving callback.
   *
   * @param { PersistenceErrorCallback } callback called when error
   */
  static notifyOnError(callback = undefined) {
    PersistenceV2.persistenceV2Impl_.notifyOnError(callback);
  }
}

PersistenceV2.persistenceV2Impl_ = PersistenceV2Impl.instance();

const Type = __Type__;

/**
 * UIUtils is a state management tool class for operating the observed data.
 *
 * @syscap SystemCapability.ArkUI.ArkUI.Full
 * @crossplatform
 * @atomicservice
 * @since 12
 */
class UIUtils {
  /**
   * Get raw object from the Object wrapped with Proxy added by statemanagement framework.
   * If input parameter is a regular Object without Proxy, return Object itself.
   *
   * 1. For StateManagement V1, when source is a @Observed decorated object,
   * or a Object/Array/Map/Set/Date decorated by state decorators like @State,
   * getTarget will return its raw object without Proxy.
   * 2. For StateManagement V2, when source is a Array/Map/Set/Date decorated by state decorators
   * like @Trace or @Local, getTarget will return its raw object without Proxy.
   * 3. For other situation, getTarget will return the source directly.
   *
   * @param { T } source input source Object data.
   * @returns { T } raw object from the Object wrapped with an ObservedObject.
   * @syscap SystemCapability.ArkUI.ArkUI.Full
   * @crossplatform
   * @atomicservice
   * @since 12
   */
  static getTarget(source) {
    return UIUtils.uiUtilsImpl_.getTarget(source);
  }

  /**
   * Make non-observed data into observed data.
   * Support non-observed class, JSON.parse, and collection.Set, collection.Map, collection.Array.
   *
   * @param { T } source input source object data.
   * @returns { T } proxy object from the source object data.
   * @syscap SystemCapability.ArkUI.ArkUI.Full
   * @crossplatform
   * @atomicservice
   * @since 12
   */
  static makeObserved(source) {
    return UIUtils.uiUtilsImpl_.makeObserved(source);
  }

  /**
   * Make non-observed data into observed V1 data.
   * Support non-observed class
   *
   * @param { T } source input source object data.
   * @returns { T } proxy object from the source object data.
   * @syscap SystemCapability.ArkUI.ArkUI.Full
   * @crossplatform
   * @atomicservice
   * @since 19
   */
  static makeV1Observed(source) {
      return UIUtils.uiUtilsImpl_.makeV1Observed(source);
  }

  /**
   * Enables V2 compatibility for the given object.
   * Ensures that the object and its nested properties conform to V2 behaviour.
   *
   * @param { Object } source - The object to be made V2-compatible.
   * @returns { Object } The processed object with V2 compatibility enabled.
   * @syscap SystemCapability.ArkUI.ArkUI.Full
   * @crossplatform
   * @atomicservice
   * @since 19
   */
  static enableV2Compatibility(source) {
    return UIUtils.uiUtilsImpl_.enableV2Compatibility(source);
  }

  /**
   * Make data binding either read-only or mutable.
   * Supports both simple getters for read-only data and getter-setter pairs for mutable data.

   * @param {() => T} getter - A function that returns the current value of type T.
   * @param {(newValue: T) => void} [setter] - (Optional) A function to set a new value of type T. If provided, a MutableBinding is created.
   * @returns {Binding<T> | MutableBinding<T>} A Binding<T> if no setter is provided, otherwise a MutableBinding<T>.
   * @syscap SystemCapability.ArkUI.ArkUI.Full
   * @crossplatform
   * @atomicservice
   * @since 20
   */
  static makeBinding(getter, setter) {
    return UIUtils.uiUtilsImpl_.makeBinding(getter, setter);
  }

  /**
   * Dynamically add monitor for state variable change.
   *
   * @param { object } target class instance or custom component instance.
   * @param { string | string[] } path  monitored change for state variable.
   * @param { MonitorCallback } monitorCallback the function that triggers the callback when state variable change.
   * @param { MonitorOptions} [options] the monitor configuration parameter.
   * @throws { BusinessError } 130000 - The target is not a custom component instance or V2 class instance.
   * @throws { BusinessError } 130001 - The path is invalid.
   * @throws { BusinessError } 130002 - monitorCallback is not a function or an anonymous function.
   * @static
   * @syscap SystemCapability.ArkUI.ArkUI.Full
   * @crossplatform
   * @atomicservice
   * @since 20
   */
    static addMonitor(target, path, monitorCallback, options) {
      UIUtils.uiUtilsImpl_.addMonitor(target, path, monitorCallback, options) ;
    }

  /**
   * Dynamically clear monitor callback for state variable change.
   *
   * @param { object } target class instance or custom component instance.
   * @param { string | string[] } path  monitored change for state variable.
   * @param { MonitorCallback } [monitorCallback] the function that triggers the callback when state variable change.
   * @throws { BusinessError } 130000 - The target is not a custom component instance or V2 class instance.
   * @throws { BusinessError } 130001 - The path is invalid.
   * @throws { BusinessError } 130002 - monitorCallback is not a function or an anonymous function.
   * @static
   * @syscap SystemCapability.ArkUI.ArkUI.Full
   * @crossplatform
   * @atomicservice
   * @since 20
   */
  static clearMonitor(target, path, monitorCallback) {
    UIUtils.uiUtilsImpl_.clearMonitor(target, path, monitorCallback) ;
  }

  /**
   * Runs a task and processes all resulting updates immediately
   *
   * @param { object } task to be executed
   * @static
   * @syscap SystemCapability.ArkUI.ArkUI.Full
   * @crossplatform
   * @atomicservice
   * @returns { T } The value returned by the task
   */
  static applySync(task) {
    return UIUtils.uiUtilsImpl_.applySync(task);
  }

  /**
   * Immediately processes all updates
   *
   * @static
   * @syscap SystemCapability.ArkUI.ArkUI.Full
   * @crossplatform
   * @atomicservice
   */
  static flushUpdates() {
    return UIUtils.uiUtilsImpl_.flushUpdates();
  }


  /**
   * Immediately rerenders UINodes that need updates
   *
   * @static
   * @syscap SystemCapability.ArkUI.ArkUI.Full
   * @crossplatform
   * @atomicservice
   */
  static flushUIUpdates() {
    return UIUtils.uiUtilsImpl_.flushUIUpdates(); 
  }

}

UIUtils.uiUtilsImpl_ = UIUtilsImpl.instance();

export default {
  AppStorageV2,
  PersistenceV2,
  Type,
  UIUtils
};
